// RayTracer.cpp : Defines the entry point for the console application.
//

#define _USE_MATH_DEFINES
#include <Windows.h>
#include <math.h>
#include <SFML/Window.hpp>
#include <gl/glut.h>
///#include "Structures.h"
#include "Shape.h"
#include <stdio.h>
#include <stdlib.h>
#include "vector.h"
#include "Tracer.h"
#include "FileReader.h"

void copyPoint3(Point3, Point3 *);
void disp(void);
void initCamera(Point3, Point3);

Color3 *bits;
int width = 500, height = 500; // window size
double angle; // View angle between the screen center and the top of screen
Point3 **rayDirections;
Color4 **colors;

int main(int argc, char** argv)
{

	glutInit(&argc , argv);
	glutInitWindowSize(width , height);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA | GLUT_DEPTH);

	// init camera
	Point3 eye(0, 0, 0);
	Point3 eyeDirection(0, 0, -1);
	angle = 30;
	initCamera(eye, eyeDirection);
	
	// allocate memory to bits to store colors
    bits = (Color3 *)malloc(sizeof(Color3)*height*width);

	glutCreateWindow("Ray Tracer");
	glutDisplayFunc(disp);
	glutMainLoop();
	free(bits);
    
    delete [] rayDirections;
    delete [] colors;

	return 0;

}

void initCamera(Point3 eye, Point3 eyeDirection)
{
    int y;
	double pixelSize;
	Point3 screenCenter;
	Point3 screenOrigin;
	Point3 yUnit, xUnit;
	Point3 imageHalfY, imageHalfX;

	pixelSize = tan(angle * M_PI / 180) / (height / 2.0);
	normalize(&eyeDirection);
	addPoints(eye, eyeDirection, &screenCenter);
	yUnit.x = 0;
	yUnit.y = 1;
	yUnit.z =  screenCenter.z - (eyeDirection.x*(-screenCenter.x) + eyeDirection.y*(yUnit.y-screenCenter.y))/eyeDirection.z;
	subtractPoints(yUnit, screenCenter, &yUnit);
	normalize(&yUnit);
	scalePoint(yUnit, pixelSize, &yUnit);

	xUnit.x = 1;
	xUnit.y = 0;
	xUnit.z =  screenCenter.z - (eyeDirection.x*(xUnit.x-screenCenter.x) + eyeDirection.y*(-screenCenter.y))/eyeDirection.z;
	subtractPoints(xUnit, screenCenter, &xUnit);
	normalize(&xUnit);
	scalePoint(xUnit, pixelSize, &xUnit);
	
	scalePoint(yUnit, -height/2.0, &imageHalfY);
	scalePoint(xUnit, -width/2.0, &imageHalfX);
	copyPoint3(screenCenter, &screenOrigin);
	addPoints(screenOrigin, imageHalfY, &screenOrigin);
	addPoints(screenOrigin, imageHalfX, &screenOrigin);
	

    rayDirections = (Point3 **)malloc(sizeof(Point3*)*height);
	// Allocate memory to rayDirections
	for(y = 0; y < height; y++){
		rayDirections[y] = (Point3 *)malloc(sizeof(Point3)*width);
	}

    colors = (Color4 **)malloc(sizeof(Color4*)*height);
	for (y = 0; y < height; y++){
		colors[y] = (Color4 *)malloc(sizeof(Color4)*width);
	}
    vector<Object*> objects = readFileforObjects("scene.txt");
    vector<LightPoint> lights = readFileforLights("scene.txt");
	rayTracer rt(objects, lights);
	// Calculate each ray direction
	for(y = 0; y < height; y++){
		for(int x = 0; x < width; x++){
			Point3 vy, vx;
			scalePoint(yUnit, y, &vy);
			scalePoint(xUnit, x, &vx);
			addPoints(screenOrigin, vy, &rayDirections[y][x]);
			addPoints(rayDirections[y][x], vx, &rayDirections[y][x]);
			subtractPoints(rayDirections[y][x], eye, &rayDirections[y][x]);
			ray3 r(eye, rayDirections[y][x]);
			colors[y][x] = rt.trace(r, 0);
		}
	}

    //delete objects
    objects.clear();
}

void disp( void ) {
    int y = 0;
    int x =0;
    for(y = 0; y < height; y++){
        for(x = 0; x < width; x++){
            bits[y*width+x].red = colors[y][x].red;
            bits[y*width+x].green = colors[y][x].green;
            bits[y*width+x].blue = colors[y][x].blue;
        }
    }

	glClear(GL_COLOR_BUFFER_BIT);
	glRasterPos2i(-1 , -1);
    glDrawPixels(width , height , GL_RGB , GL_FLOAT , bits);
	glFlush();
}

void copyPoint3(Point3 in, Point3 *out)
{
	out->x = in.x;
	out->y = in.y;
	out->z = in.z;
}
